/*--------------------------------------------------------------
 * File: lvgl_task.c 编码类型：C语言  UTF-8
 * Author WRS (1352509846@qq.com)
 * 功能:
 * Version 0.1
 * Date 2024-04-23
 *
 * @copyright Copyright (c) 2024
 *
----------------------------------------------------------------*/
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "freertos/event_groups.h"
#include "esp_lcd_panel_io.h"
#include "esp_lcd_panel_vendor.h"
#include "esp_lcd_panel_ops.h"
#include "driver/gpio.h"
#include "driver/spi_master.h"
#include "esp_lcd_ili9341.h"
#include "esp_timer.h"
#include "esp_err.h"
#include "esp_log.h"
#include "lvgl.h"
#include "ui.h"
#include "lvgl_task.h"
#include "sntp_task.h"
#include "sta.h"

static const char *TAG = "LVGL Task";
extern wifi_info_t Connect_wifinfo;  // 连接的wifi信息

// FreeRTOS事件组，连接成功时发出信号
EventGroupHandle_t ui_wifi_event_group = NULL;

uint64_t LockScreen = 0;

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////// Please update the following configuration according to your LCD spec //////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#define EXAMPLE_LCD_PIXEL_CLOCK_HZ (40 * 1000 * 1000)
#define EXAMPLE_LCD_BK_LIGHT_ON_LEVEL 1
#define EXAMPLE_LCD_BK_LIGHT_OFF_LEVEL !EXAMPLE_LCD_BK_LIGHT_ON_LEVEL
#define EXAMPLE_PIN_NUM_SCLK 18
#define EXAMPLE_PIN_NUM_MOSI 23
#define EXAMPLE_PIN_NUM_MISO -1
#define EXAMPLE_PIN_NUM_LCD_DC 27
#define EXAMPLE_PIN_NUM_LCD_RST 33
#define EXAMPLE_PIN_NUM_LCD_CS 14
#define EXAMPLE_PIN_NUM_BK_LIGHT 32
#define EXAMPLE_PIN_NUM_TOUCH_CS 15

// The pixel number in horizontal and vertical
#define EXAMPLE_LCD_H_RES 320
#define EXAMPLE_LCD_V_RES 240

// Bit number used to represent command and parameter
#define EXAMPLE_LCD_CMD_BITS 8
#define EXAMPLE_LCD_PARAM_BITS 8

#define EXAMPLE_LVGL_TICK_PERIOD_MS 5

// Using SPI2 in the example
#define LCD_HOST SPI2_HOST

#define KEY_PIN_LIFT    39
#define KEY_PIN_ENTER   38
#define KEY_PIN_RIGHT   37

SemaphoreHandle_t Lvgl_xMutex;   // 互斥锁

lv_indev_t * indev_keypad;
// lv_group_t * group_lock;
lv_group_t * group1;    // 界面1的组
lv_group_t * group2;    // 界面2的组
lv_group_t * group_ML04; // ML04的组
lv_group_t * group_ML12; // ML12的组
lv_group_t * group_MLD6; // MLD6的组
lv_group_t * group_MD8;  // MD8的组
lv_group_t * group_MA8;  // MA8的组
lv_group_t * group_M26;  // M26的组
lv_group_t * group_M38;  // M38的组



static void Lvgl_event_group_init(void)
{
    // 创建一个事件组，用于管理Wi-Fi事件。
    ui_wifi_event_group = xEventGroupCreate();
}

static void Lvgl_event_group_Check(void)
{
    EventBits_t bits = xEventGroupWaitBits(ui_wifi_event_group, WIFI_SCAN_DONE_BIT | WIFI_CONNECTED_BIT | WIFI_DISCONNECTED_BIT,
                                           pdFALSE, pdFALSE, 0);

    // if (bits & WIFI_SCAN_DONE_BIT)
    // {
    //     ESP_LOGI(TAG,"WIFI_SCAN_DONE_BIT event triggered\n");
    //     ui_SCR2Wifi_List_Deinit();
    //     ui_SCR2Wifi_List_Init();
    // }
    // if (bits & WIFI_CONNECTED_BIT)
    // {
    //     ESP_LOGI(TAG,"WIFI_CONNECTED_BIT event triggered\n");
    //     Connect_wifinfo.wifi_connect_status = 1;
    //     lv_event_send(ui_InfoLabel1, LV_EVENT_REFRESH, &Connect_wifinfo.wifi_connect_status);
    // }
    if (bits & WIFI_DISCONNECTED_BIT)
    {
        ESP_LOGI(TAG,"WIFI_DISCONNECTED_BIT event triggered\n");
        lv_label_set_text_fmt(ui_IPLabel1, " ");
    }
    if (bits & WIFI_GOT_IP_BIT)
    {
        ESP_LOGI(TAG,"WIFI_GOT_IP_BIT event triggered\n");
        // lv_label_set_text_fmt(ui_IPLabel1, "%s", wifi_get_ip_info(WIFI_IF_STA).ipaddr);
        lv_label_set_text_fmt(ui_IPLabel1, "%s", Connect_wifinfo.IP);
    }

    // // 清除处理完成的事件
    xEventGroupClearBits(ui_wifi_event_group, bits);
}

/*--------------------------------------------------------------
 * 功能: 设置控件的style
 *
----------------------------------------------------------------*/
static void UI_Set_Lvgl_style(void)
{
    lv_obj_set_style_outline_color(ui_Scr1BML04_btn, lv_color_hex(0xFF0000), LV_PART_MAIN | LV_STATE_FOCUS_KEY);
    lv_obj_set_style_outline_color(ui_Scr1BML12_btn, lv_color_hex(0xFF0000), LV_PART_MAIN | LV_STATE_FOCUS_KEY);
    lv_obj_set_style_outline_color(ui_Scr1MD8_btn, lv_color_hex(0xFF0000), LV_PART_MAIN | LV_STATE_FOCUS_KEY);
    lv_obj_set_style_outline_color(ui_Scr1MA8_btn, lv_color_hex(0xFF0000), LV_PART_MAIN | LV_STATE_FOCUS_KEY);
    lv_obj_set_style_outline_color(ui_Scr1M38_btn, lv_color_hex(0xFF0000), LV_PART_MAIN | LV_STATE_FOCUS_KEY);
    lv_obj_set_style_outline_color(ui_Scr1M26_btn, lv_color_hex(0xFF0000), LV_PART_MAIN | LV_STATE_FOCUS_KEY);
    lv_obj_set_style_outline_color(ui_Set_DevUart_Dpdown, lv_color_hex(0xFF0000), LV_PART_MAIN | LV_STATE_FOCUS_KEY);
    lv_obj_set_style_outline_color(ui_Set_SD_Dpdown, lv_color_hex(0xFF0000), LV_PART_MAIN | LV_STATE_FOCUS_KEY);
    lv_obj_set_style_outline_color(ui_Scr1_ClearAddrBtn, lv_color_hex(0xFF0000), LV_PART_MAIN | LV_STATE_FOCUS_KEY);
    lv_obj_set_style_outline_color(ui_Scr1_Btn1_StartStop, lv_color_hex(0xFF0000), LV_PART_MAIN | LV_STATE_FOCUS_KEY);

    // lv_obj_set_style_outline_color(ui_SCR2DropDown, lv_color_hex(0xFF0000), LV_PART_MAIN | LV_STATE_FOCUS_KEY);
    // lv_obj_set_style_outline_color(ui_SCR2Btn1, lv_color_hex(0xFF0000), LV_PART_MAIN | LV_STATE_FOCUS_KEY);
    // lv_obj_set_style_outline_color(ui_SCR2Btn2, lv_color_hex(0xFF0000), LV_PART_MAIN | LV_STATE_FOCUS_KEY);
}

static bool example_notify_lvgl_flush_ready(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_io_event_data_t *edata, void *user_ctx)
{
    lv_disp_drv_t *disp_driver = (lv_disp_drv_t *)user_ctx;
    lv_disp_flush_ready(disp_driver);
    return false;
}

static void example_lvgl_flush_cb(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map)
{
    esp_lcd_panel_handle_t panel_handle = (esp_lcd_panel_handle_t) drv->user_data;
    int offsetx1 = area->x1;
    int offsetx2 = area->x2;
    int offsety1 = area->y1;
    int offsety2 = area->y2;
    // copy a buffer's content to a specific area of the display
    esp_lcd_panel_draw_bitmap(panel_handle, offsetx1, offsety1, offsetx2 + 1, offsety2 + 1, color_map);
}

/* Rotate display and touch, when rotated screen in LVGL. Called when driver parameters are updated. */
static void example_lvgl_port_update_callback(lv_disp_drv_t *drv)
{
    esp_lcd_panel_handle_t panel_handle = (esp_lcd_panel_handle_t) drv->user_data;

    switch (drv->rotated) {
    case LV_DISP_ROT_NONE:
        // Rotate LCD display
        esp_lcd_panel_swap_xy(panel_handle, false);
        esp_lcd_panel_mirror(panel_handle, false, false);
#if CONFIG_EXAMPLE_LCD_TOUCH_ENABLED
        // Rotate LCD touch
        esp_lcd_touch_set_mirror_y(tp, false);
        esp_lcd_touch_set_mirror_x(tp, false);
#endif
        break;
    case LV_DISP_ROT_90:
        // Rotate LCD display
        esp_lcd_panel_swap_xy(panel_handle, true);
        esp_lcd_panel_mirror(panel_handle, true, false);
#if CONFIG_EXAMPLE_LCD_TOUCH_ENABLED
        // Rotate LCD touch
        esp_lcd_touch_set_mirror_y(tp, false);
        esp_lcd_touch_set_mirror_x(tp, false);
#endif
        break;
    case LV_DISP_ROT_180:
        // Rotate LCD display
        esp_lcd_panel_swap_xy(panel_handle, false);
        esp_lcd_panel_mirror(panel_handle, true, true);
#if CONFIG_EXAMPLE_LCD_TOUCH_ENABLED
        // Rotate LCD touch
        esp_lcd_touch_set_mirror_y(tp, false);
        esp_lcd_touch_set_mirror_x(tp, false);
#endif
        break;
    case LV_DISP_ROT_270:
        // Rotate LCD display
        esp_lcd_panel_swap_xy(panel_handle, true);
        esp_lcd_panel_mirror(panel_handle, false, true);
#if CONFIG_EXAMPLE_LCD_TOUCH_ENABLED
        // Rotate LCD touch
        esp_lcd_touch_set_mirror_y(tp, false);
        esp_lcd_touch_set_mirror_x(tp, false);
#endif
        break;
    }
}

static void example_increase_lvgl_tick(void *arg)
{
    /* Tell LVGL how many milliseconds has elapsed */
    lv_tick_inc(EXAMPLE_LVGL_TICK_PERIOD_MS);
}

/*Initialize your keypad*/
static void keypad_init(void)
{
    /*Your code comes here*/
    // gpio_set_direction(KEY_PIN_LIFT, GPIO_MODE_INPUT);
    gpio_config_t io_conf;

    // 配置按键1的 GPIO 引脚
    io_conf.intr_type = GPIO_INTR_DISABLE;
    io_conf.mode = GPIO_MODE_INPUT;
    io_conf.pin_bit_mask = (1ULL << KEY_PIN_LIFT);
    io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
    io_conf.pull_up_en = GPIO_PULLUP_ENABLE;
    gpio_config(&io_conf);
    // 配置按键2的 GPIO 引脚
    io_conf.pin_bit_mask = (1ULL << KEY_PIN_ENTER);
    gpio_config(&io_conf);
    // 配置按键3的 GPIO 引脚
    io_conf.pin_bit_mask = (1ULL << KEY_PIN_RIGHT);
    gpio_config(&io_conf);
}

/*Get the currently being pressed key.  0 if no key is pressed*/
static uint32_t keypad_get_key(const lv_group_t * group)
{
    /*Your code comes here*/
    if(gpio_get_level(KEY_PIN_RIGHT)==0)
    {
        LockScreen = 0;
        // ESP_LOGI(TAG, "KEY_PIN_RIGHT press\n");
        if(lv_group_get_editing(group)==1)  // 如果是编辑模式
            return 4;
        else
            return 1;
    }

    if(gpio_get_level(KEY_PIN_LIFT)==0) 
    {
        LockScreen = 0;
        // ESP_LOGI(TAG, "KEY_PIN_LIFT press\n");
        if(lv_group_get_editing(group)==1)  // 如果是编辑模式
            return 3;
        else
            return 2;
    }
    
    if(gpio_get_level(KEY_PIN_ENTER)==0)
    {
        LockScreen = 0;
        // ESP_LOGI(TAG, "KEY_PIN_ENTER press\n");
        return 5;
    }
    return 0;
}

/*Will be called by the library to read the mouse*/
static void keypad_read(lv_indev_drv_t *indev_drv, lv_indev_data_t *data)
{
    static uint32_t last_key = 0;
    uint32_t act_key = 0;
    /*Get the current x and y coordinates*/

    /*Get whether the a key is pressed and save the pressed key*/
    
    lv_obj_t * active_screen = lv_scr_act();
    if(active_screen == ui_SCR1)
    {
        act_key = keypad_get_key(group1);
    }
    // else if(active_screen == ui_SCR2)
    // {
    //     act_key = keypad_get_key(group2);
    // }
    else if(active_screen == ui_Scr3_ML04)
    {
        act_key = keypad_get_key(group_ML04);
    }
    else if(active_screen == ui_Scr4_ML12)
    {
        act_key = keypad_get_key(group_ML12);
    }
    else if(active_screen == ui_Scr5_MD8)
    {
        act_key = keypad_get_key(group_MD8);
    }
    else if(active_screen == ui_Scr6_MA8)
    {
        act_key = keypad_get_key(group_MA8);
    }
    else if(active_screen == ui_Scr7_M26)
    {
        act_key = keypad_get_key(group_M26);
    }
    else if(active_screen == ui_Scr8_M38)
    {
        act_key = keypad_get_key(group_M38);
    }

    if (act_key != 0)
    {
        data->state = LV_INDEV_STATE_PR;

        /*Translate the keys to LVGL control characters according to your key definitions*/
        switch (act_key)
        {
            case 1:
                act_key = LV_KEY_NEXT;  // 切换下一个控件
                break;
            case 2:
                act_key = LV_KEY_PREV;  // 切换上一个控件
                break;
            case 3:
                act_key = LV_KEY_LEFT;  // 修改值减小
                break;
            case 4:
                act_key = LV_KEY_RIGHT; // 修改值增加
                break;
            case 5:
                act_key = LV_KEY_ENTER; // 确认
                break;   
        }
        last_key = act_key;
    }
    else
    {
        data->state = LV_INDEV_STATE_REL;
    }

    data->key = last_key;
}

static void lvgl_task(void *parm)
{
    static lv_disp_draw_buf_t disp_buf; // contains internal graphic buffer(s) called draw buffer(s)
    static lv_disp_drv_t disp_drv;      // contains callback functions
//---------------------------------------------------------------------------------------------------------------
    ESP_LOGI(TAG, "Turn off LCD backlight");
    gpio_config_t bk_gpio_config =
        {
            .mode = GPIO_MODE_OUTPUT,
            .pin_bit_mask = 1ULL << EXAMPLE_PIN_NUM_BK_LIGHT};
    ESP_ERROR_CHECK(gpio_config(&bk_gpio_config));
//---------------------------------------------------------------------------------------------------------------
    ESP_LOGI(TAG, "Initialize SPI bus");    // SPI总线在SD卡初始化里执行的初始化
    // spi_bus_config_t buscfg = {
    //     .sclk_io_num = EXAMPLE_PIN_NUM_SCLK,
    //     .mosi_io_num = EXAMPLE_PIN_NUM_MOSI,
    //     .miso_io_num = EXAMPLE_PIN_NUM_MISO,
    //     .quadwp_io_num = -1,
    //     .quadhd_io_num = -1,
    //     .max_transfer_sz = EXAMPLE_LCD_H_RES * 80 * sizeof(uint16_t),
    // };
    // ESP_ERROR_CHECK(spi_bus_initialize(LCD_HOST, &buscfg, SPI_DMA_CH_AUTO));
//---------------------------------------------------------------------------------------------------------------
    ESP_LOGI(TAG, "Install panel IO");
    esp_lcd_panel_io_handle_t io_handle = NULL;
    esp_lcd_panel_io_spi_config_t io_config = {
        .dc_gpio_num = EXAMPLE_PIN_NUM_LCD_DC,
        .cs_gpio_num = EXAMPLE_PIN_NUM_LCD_CS,
        .pclk_hz = EXAMPLE_LCD_PIXEL_CLOCK_HZ,
        .lcd_cmd_bits = EXAMPLE_LCD_CMD_BITS,
        .lcd_param_bits = EXAMPLE_LCD_PARAM_BITS,
        .spi_mode = 0,
        .trans_queue_depth = 10,
        .on_color_trans_done = example_notify_lvgl_flush_ready,
        .user_ctx = &disp_drv,
    };
    // Attach the LCD to the SPI bus
    ESP_ERROR_CHECK(esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)LCD_HOST, &io_config, &io_handle));

    esp_lcd_panel_handle_t panel_handle = NULL;
    esp_lcd_panel_dev_config_t panel_config = {
        .reset_gpio_num = EXAMPLE_PIN_NUM_LCD_RST,
        .rgb_ele_order = LCD_RGB_ELEMENT_ORDER_BGR,
        .bits_per_pixel = 16,
    };
//---------------------------------------------------------------------------------------------------------------
    ESP_LOGI(TAG, "Install ILI9341 panel driver");
    ESP_ERROR_CHECK(esp_lcd_new_panel_ili9341(io_handle, &panel_config, &panel_handle));
    ESP_ERROR_CHECK(esp_lcd_panel_reset(panel_handle));
    ESP_ERROR_CHECK(esp_lcd_panel_init(panel_handle));
    ESP_ERROR_CHECK(esp_lcd_panel_mirror(panel_handle, false, false));
    // user can flush pre-defined pattern to the screen before we turn on the screen or backlight
    ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(panel_handle, true));

    ESP_LOGI(TAG, "Turn on LCD backlight");
    gpio_set_level(EXAMPLE_PIN_NUM_BK_LIGHT, EXAMPLE_LCD_BK_LIGHT_ON_LEVEL);
//---------------------------------------------------------------------------------------------------------------
    ESP_LOGI(TAG, "Initialize LVGL library");
    lv_init();
    // alloc draw buffers used by LVGL
    // it's recommended to choose the size of the draw buffer(s) to be at least 1/10 screen sized
    lv_color_t *buf1 = heap_caps_malloc(EXAMPLE_LCD_H_RES * 30 * sizeof(lv_color_t), MALLOC_CAP_DMA);
    assert(buf1);
    lv_color_t *buf2 = heap_caps_malloc(EXAMPLE_LCD_H_RES * 30 * sizeof(lv_color_t), MALLOC_CAP_DMA);
    assert(buf2);
    // initialize LVGL draw buffers
    lv_disp_draw_buf_init(&disp_buf, buf1, buf2, EXAMPLE_LCD_H_RES * 30);
//---------------------------------------------------------------------------------------------------------------
    ESP_LOGI(TAG, "Register display driver to LVGL");
    lv_disp_drv_init(&disp_drv);
    disp_drv.hor_res = EXAMPLE_LCD_H_RES;
    disp_drv.ver_res = EXAMPLE_LCD_V_RES;
    disp_drv.flush_cb = example_lvgl_flush_cb;
    disp_drv.drv_update_cb = example_lvgl_port_update_callback;
    disp_drv.draw_buf = &disp_buf;
    disp_drv.user_data = panel_handle;
    lv_disp_drv_register(&disp_drv);
//---------------------------------------------------------------------------------------------------------------
    ESP_LOGI(TAG, "Install LVGL tick timer");
    // Tick interface for LVGL (using esp_timer to generate 2ms periodic event)
    const esp_timer_create_args_t lvgl_tick_timer_args = {
        .callback = &example_increase_lvgl_tick,
        .name = "lvgl_tick"
    };
    esp_timer_handle_t lvgl_tick_timer = NULL;
    ESP_ERROR_CHECK(esp_timer_create(&lvgl_tick_timer_args, &lvgl_tick_timer));
    ESP_ERROR_CHECK(esp_timer_start_periodic(lvgl_tick_timer, EXAMPLE_LVGL_TICK_PERIOD_MS * 1000));
//---------------------------------------------------------------------------------------------------------------
    /*Register a keypad input device*/
    ESP_LOGI(TAG, "Initialize KEY");
    static lv_indev_drv_t indev_drv;    // Input device driver (KEY)
    keypad_init();
    lv_indev_drv_init(&indev_drv);
    indev_drv.type = LV_INDEV_TYPE_KEYPAD;
    indev_drv.read_cb = keypad_read;
    indev_drv.long_press_time = 2000;
    indev_keypad = lv_indev_drv_register(&indev_drv);

    /*创建按键组*/
    // group_lock = lv_group_create();
    group1 = lv_group_create();
    // group2 = lv_group_create();
    group_ML04 = lv_group_create();
    group_ML12 = lv_group_create();
    group_MD8 = lv_group_create();
    group_MA8 = lv_group_create();
    group_M26 = lv_group_create();
    group_M38 = lv_group_create();


    // lv_group_set_default(group2);
    
    ui_init();
    UI_Set_Lvgl_style();

    // 添加控件到组里
    // lv_group_add_obj(group_lock, time_date_obj);

    lv_group_add_obj(group1, ui_Scr1BML04_btn);
    lv_group_add_obj(group1, ui_Scr1BML12_btn);
    lv_group_add_obj(group1, ui_Scr1MD8_btn);
    lv_group_add_obj(group1, ui_Scr1MA8_btn);
    lv_group_add_obj(group1, ui_Scr1M26_btn);
    lv_group_add_obj(group1, ui_Scr1M38_btn);    
    lv_group_add_obj(group1, ui_Set_DevUart_Dpdown);
    lv_group_add_obj(group1, ui_Set_SD_Dpdown);
    lv_group_add_obj(group1, ui_Scr1_ClearAddrBtn);
    lv_group_add_obj(group1, ui_Scr1_Btn1_StartStop);
    
    // lv_group_add_obj(group2, ui_SCR2DropDown);
    // lv_group_add_obj(group2, ui_SCR2Btn1);  
    // lv_group_add_obj(group2, ui_SCR2Btn2);

    lv_indev_set_group(indev_keypad, group1);

    // 创建互斥量
    Lvgl_xMutex = xSemaphoreCreateMutex();
    Lvgl_event_group_init();

    while (1) 
    {
        // raise the task priority of LVGL and/or reduce the handler period can improve the performance
        vTaskDelay(1);
        // The task running lv_timer_handler should have lower priority than that running `lv_tick_inc`
        // 等待互斥量
        if(xSemaphoreTake(Lvgl_xMutex, portMAX_DELAY) == pdTRUE) 
        {
            Lvgl_event_group_Check();
            lv_timer_handler();

            // 释放互斥量
            xSemaphoreGive(Lvgl_xMutex);
        }
    }
}

void Lvgl_task_Init(void)
{
    xTaskCreatePinnedToCore(&lvgl_task, "lvgl_task", 8192, NULL, 10, NULL, 1);    
}
